#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import TypeVar, Generic, List

from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.db.base_class import Base

ModelType = TypeVar("ModelType", bound=Base)
QueryType = TypeVar("QueryType", bound=BaseModel)
SaveType = TypeVar("SaveType", bound=BaseModel)
from app.common.page import PageParam, PageResult


class BaseDao(Generic[ModelType]):
    def __init__(self):
        self.modelClass = None
        self.id = None

    def insert(self, db: Session, entity: SaveType) -> ModelType:
        data = entity.dict(exclude_none=True)
        db_data = self.modelClass(**data)
        db.add(db_data)
        db.flush()
        return db_data


    def insertBatch(self, db: Session, entities: List[SaveType]) -> List[ModelType]:
        db_datas = []
        for entity in entities:
            db_datas.append(self.insert(db, entity))
        return db_datas

    def deleteBatchIds(self, db: Session, ids) -> int:
        result = db.query(self.modelClass).filter(self.id._in(ids)).delete()
        return result

    def deleteById(self, db: Session, id) -> int:
        return db.query(self.modelClass).filter(self.id == id).delete()

    def delete(self, db: Session, queryWrapper: QueryType) -> int:
        params = queryWrapper.dict(exclude_none=True)
        return db.query(self.modelClass).filter_by(**params).delete()

    def updateById(self, db: Session, entity: SaveType) -> ModelType:
        updateObj = entity.dict(exclude_none=True)
        id = updateObj[self.id.key]
        del updateObj[self.id.key]
        return db.query(self.modelClass).filter(self.id == id).update(updateObj)

    def update(self, db: Session, entity: SaveType, queryWrapper: QueryType):
        params = queryWrapper.dict(exclude_none=True)
        updateObj = entity.dict(exclude_none=True)
        return db.query(self.modelClass).filter_by(**params).update(updateObj)

    def selectById(self, db: Session, id) -> ModelType:
        return db.query(self.modelClass).filter(self.id == id).first()

    def selectBatchIds(self, db: Session, ids, orderBy: tuple = ()) -> List[ModelType]:
        return db.query(self.modelClass).filter(self.in_(ids)).order_by(*orderBy).all()

    def select(self, db: Session, queryWrapper: QueryType, orderBy: tuple = ()) -> List[ModelType]:
        params = queryWrapper.dict(exclude_none=True)
        return db.query(self.modelClass).filter_by(**params).order_by(*orderBy).all()

    def selectOne(self, db: Session, queryWrapper: QueryType, orderBy: tuple = ()) -> ModelType:
        params = queryWrapper.dict(exclude_none=True)
        return db.query(self.modelClass).filter_by(**params).order_by(*orderBy).first()

    def selectAll(self, db: Session, orderBy: tuple = ()) -> List[ModelType]:
        return db.query(self.modelClass).order_by(*orderBy).all()

    def page(self, db: Session, pageParam: PageParam, queryWrapper: QueryType, orderBy: tuple = None)->PageResult[List[ModelType]]:
        params = queryWrapper.dict(exclude_none=True)
        models = db.query(self.modelClass) \
            .filter_by(**params) \
            .order_by(*orderBy) \
            .offset((pageParam.pageNum - 1) * pageParam.pageSize) \
            .limit(pageParam.pageSize).all()
        total = db.query(self.modelClass).filter_by(**params).count()
        pageResult = PageResult(pageNum=pageParam.pageNum, pageSize=pageParam.pageSize, total=total, items=models)
        return pageResult

    def pageAll(self, db: Session, pageParam: PageParam, orderBy: tuple = None) -> PageResult[List[ModelType]]:
        models = db.query(self.modelClass).order_by(*orderBy) \
            .offset((pageParam.pageNum - 1) * pageParam.pageSize).all()
        total = db.query(self.modelClass).count()
        pageResult = PageResult(pageNum=pageParam.pageNum, pageSize=pageParam.pageSize, total=total, items=models)
        return pageResult

    def selectCount(self, db: Session, queryWrapper: QueryType) -> int:
        params = queryWrapper.dict(exclude_none=True)
        return db.query(self.modelClass).filter_by(**params).count()
